﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace KontoraME
{
    public partial class AddStage : Form
    {
        const float SCALE_RATE = 0.5f;

        //Biến lưu vị trí của map
        private string mapLocation;
        //Bitmap chứa hình nền của stage
        private Bitmap background;
        //Đối tượng quản lý tiến trình cắt tiles của background
        private BackgroundWorker bgWorker;
        //Ma trận lưu tile id
        private int[,] tileMatrix;
        //Ma trận lưu box id
        private int[,] boxMatrix;
        //Đối tượng WorldMap
        WorldMap world;
        //Bitmap lưu hình tiles sau khi cắt
        private Bitmap tiles;
        //Số hàng của Stage Map
        private int rows;
        //Số cột của Stage Map
        private int cols;

        public AddStage(WorldMap wm)
        {
            InitializeComponent();
            this.world = wm;
        }

        //Load hình nền của map
        private void btnLoadBackGround_Click(object sender, EventArgs e)
        {
            OpenFileDialog dg = new OpenFileDialog();
            dg.Filter = "Image Files (*.bmp; *.png; *.jpg; *.gif)|*.bmp; *.png; *.jpg; *.gif";
            dg.Multiselect = false;
            if (dg.ShowDialog() == DialogResult.OK)
            {
                // Lưu các thông số để vẽ hình nền lên pnImage
                mapLocation = dg.FileName;
                this.background = (Bitmap)System.Drawing.Image.FromFile(mapLocation, true);
                //Scale background lại cho vừa với pnImage
                Rectangle r = new
                    Rectangle(0, 0, (int)(background.Width * SCALE_RATE), (int)(background.Height * SCALE_RATE));
                //Bitmap afterScale = background.Clone(r, background.PixelFormat);
                Bitmap tmpBmp = new
                    Bitmap((int)(background.Width * SCALE_RATE), (int)(background.Height * SCALE_RATE));
                using (Graphics g = Graphics.FromImage(tmpBmp))
                {
                    g.InterpolationMode = InterpolationMode.NearestNeighbor;
                    g.DrawImage(background, 0, 0, (int)(background.Width * SCALE_RATE), (int)(background.Height * SCALE_RATE));
                    background = tmpBmp;
                }


                //Cắt tiles
                bgWorker = new BackgroundWorker();
                bgWorker.DoWork += bgWorker_DoWork;
                bgWorker.ProgressChanged += bgWorker_ProgressChanged;
                bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
                //Cho phép báo cáo tiến trình thực hiện doWork
                bgWorker.WorkerReportsProgress = true;


                this.rtbSetUp.Text = "";
                tiles = new Bitmap(240, 400);
                this.pbTiles.Image = tiles;
                pnImage.Controls.Clear();

                //Vẽ hình nền lên pnImage
                PictureBox pb_background = new PictureBox();
                pb_background.Image = background;
                pb_background.Location = new Point(0, 0);
                pb_background.SizeMode = PictureBoxSizeMode.AutoSize;
                pnImage.Controls.Add(pb_background);

                //Chạy background worker
                bgWorker.RunWorkerAsync();
            }
        }

        //Hàm xử lý khi tiến trình hoàn thành
        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.pbgStatus.Value = 1000;
            rtbSetUp.SelectionStart = rtbSetUp.Text.Length;
            rtbSetUp.ScrollToCaret();

            //Tạo Bitmap lưu các tiles đã cắt ra với
            //số cột = tổng số tile / sqrt(tổng số tile)
            //số hàng = tổng số tile / số cột (cộng 1 nếu tổng số tile chia hết số cột)
            int index = world.listTiles.Count;
            int afterCol = index / Int16.Parse(Math.Round(Math.Sqrt(index), 0).ToString());
            int afterRow = index % afterCol == 0 ? index / afterCol: index / afterCol + 1;
            tiles = new Bitmap(afterCol * GlobalConstants.TILE_SIZE, afterRow * GlobalConstants.TILE_SIZE);

            //Vẽ các đối tượng tile vào trong Bitmap
            int height = (1 + index / afterCol) * afterRow;
            foreach (Bitmap bm in world.listTiles)
            {
                int i = world.listTiles.IndexOf(bm) % afterCol;
                int j = world.listTiles.IndexOf(bm) / afterCol;

                using (Graphics g = Graphics.FromImage(tiles))
                {
                    g.InterpolationMode = InterpolationMode.NearestNeighbor;
                    g.DrawImage(bm, i * GlobalConstants.TILE_SIZE, j * GlobalConstants.TILE_SIZE, GlobalConstants.TILE_SIZE, GlobalConstants.TILE_SIZE);
                }
            }
            this.pbTiles.Width = tiles.Width;
            this.pbTiles.Height = tiles.Height;
            this.pbTiles.Image = tiles;
        }

        //Hàm xử lý khi ReportProgress được gọi
        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.pbgStatus.Value = e.ProgressPercentage;
            if (e.UserState != null)
            {
                this.rtbSetUp.AppendText(e.UserState as string);
                if ((e.UserState as string).IndexOf("->") > 0)
                {
                    rtbSetUp.SelectionStart = rtbSetUp.Text.Length;
                    rtbSetUp.ScrollToCaret();
                }
            }
        }

        //Hàm xử lý tiến trình cắt tiles
        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            //Đo thời gian chạy để tính progress time
            DateTime start = DateTime.UtcNow;
            try
            {
                //Load lại background theo kích thước mặc định
                this.background = (Bitmap)System.Drawing.Image.FromFile(mapLocation, true);
                Bitmap tmpBm = new Bitmap(background.Width, background.Height);
                using (Graphics g = Graphics.FromImage(tmpBm))
                {
                    g.InterpolationMode = InterpolationMode.NearestNeighbor;
                    g.DrawImage(background, new Rectangle(0, 0, background.Width, background.Height),
                        0, 0, background.Width, background.Height, GraphicsUnit.Pixel);
                }

                //Số hàng và số cột tile
                int colNum = background.Width / GlobalConstants.TILE_SIZE;
                int rowNum = background.Height / GlobalConstants.TILE_SIZE;

                this.tileMatrix = new int[rowNum, colNum];
                int p = 0;
                for (int row = 0; row < rowNum; row++)
                {
                    this.bgWorker.ReportProgress(row * 1000 / rowNum, "\n->");
                    string s = "";
                    //Duyệt từng tile để so sánh
                    for (int col = 0; col < colNum; col++) 
                    {
                        Rectangle r = new
                            Rectangle(col * GlobalConstants.TILE_SIZE, row * GlobalConstants.TILE_SIZE, GlobalConstants.TILE_SIZE, GlobalConstants.TILE_SIZE);
                        Bitmap tileBm = background.Clone(r, background.PixelFormat);
                        int k = 0;
                        bool flag = false;
                        //duyệt tất cả các tile trong listTiles, so sánh với tile hiện tại
                        foreach (Bitmap b in world.listTiles)
                            if (CompareBitmap(tileBm, b))
                            {
                                flag = true;
                                k = world.listTiles.IndexOf(b);
                                tileMatrix[row, col] = k;
                                break;
                            }
                        //Kiểm tra nếu tile chưa có trong listTiles thì thêm vào listTile
                        //(trường hợp đầu tiên)
                        if (!flag)
                        {
                            world.listTiles.Add(tileBm);
                            k = world.listTiles.IndexOf(tileBm);
                            tileMatrix[row, col] = k;
                        }
                        if (k < 10)
                            s += "  ";
                        else if (k < 100)
                            s += " ";
                        s += " " + k;

                        p = ((row * colNum + col) * 1000) / (rowNum * colNum);
                        this.bgWorker.ReportProgress(p);
                    }
                    this.bgWorker.ReportProgress(p, s);
                }
                DateTime end = DateTime.UtcNow;

                string info = "\n\n -->  Total Time  :  " + (end - start).TotalMilliseconds.ToString() + " ms";

                info += "\n      Total Tiles :  " + rowNum * colNum;

                info += "\n      Tiles Num   :  " + world.listTiles.Count;
                info += "\n      Tiles Row   :  " + rowNum;
                info += "\n      Tiles Col   :  " + colNum + "\n\n";

                cols = colNum;
                rows = rowNum;

                this.bgWorker.ReportProgress(1000, info);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace);
            }
        }

        //Hàm so sánh 2 bitmap
        public bool CompareBitmap(Bitmap bm1, Bitmap bm2)
        {
            bool result = true;
            if (bm1.Size != bm2.Size)
            {
                result = false;
            }
            Rectangle rect = new Rectangle(0, 0, GlobalConstants.TILE_SIZE, GlobalConstants.TILE_SIZE);
            BitmapData bmData1 = bm1.LockBits(rect, ImageLockMode.ReadOnly, bm1.PixelFormat);
            BitmapData bmData2 = bm2.LockBits(rect, ImageLockMode.ReadOnly, bm2.PixelFormat);
            unsafe
            {
                byte* ptr1 = (byte*)bmData1.Scan0.ToPointer();
                byte* ptr2 = (byte*)bmData2.Scan0.ToPointer();
                int width = rect.Width;
                for (int y = 0; result && y < rect.Height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (*ptr1 != *ptr2)
                        {
                            result = false;
                            break;
                        }
                        ptr1++;
                        ptr2++;
                    }
                    ptr1 += bmData1.Stride - width;
                    ptr2 += bmData2.Stride - width;
                }
            }
            bm1.UnlockBits(bmData1);
            bm2.UnlockBits(bmData2);
            return result;
        }
        
        //Xử lý button Add
        private void btnAdd_Click(object sender, EventArgs e)
        {
            Stage st = new Stage();
            st.cols = this.cols;
            st.rows = this.rows;
            st.position = new Point(0, 0);
            boxMatrix = new int[st.rows, st.cols];
            for (int i = 0; i < st.rows; i++)
                for (int j = 0; j < st.cols; j++)
                    boxMatrix[i, j] = 0;
            st.matrixTiles = tileMatrix;
            st.matrixBody = boxMatrix;
            world.ListStages.Add(st);
            this.Close();
        }
    }
}
